/*
 *  tf.cpp
 *  perceel
 *
 *  Created by Nicola Gigante on 13/03/08.
 *  Copyright 2008 Nicola Gigante. All rights reserved.
 *
 */

#include "tf.h"

/*!
 Questo costruttore crea la fdt identica ad un'altra già esistente.
 Vengono direttamente copiate le liste degli zeri e dei poli e ricreati
 di conseguenza i polinomi numeratore e denominatore.
 */
TF::TF(const TF &tf)
{
	m_roots = tf.m_roots;
	updateNumerator();
	if(tf.isFract())
	{
		m_poles = tf.m_poles;
		updateDenominator();
	}else
		m_den = 1;
	m_viewMode = tf.m_viewMode;
}

/*!
 Con questo costruttore si crea una fdt partendo dai due polinomi numeratore
 e denominatore. Il parametro vm non viene attualmente usato e può essere lasciato
 al valore di default.
 \see setNumerator(Polynomial)
 \see setDenominator(Polynomial)
 */
TF::TF(Polynomial num, Polynomial den, ViewMode vm)
{
	setNumerator(num);
	setDenominator(den);
	m_viewMode = vm;
}

/*!
 Aggiungere uno zero alla fdt necessita di due fasi.
	-# viene aggiunto effettivamente lo zero alla lista degli zeri.
	-# viene ricalcolato il polinomio numeratore.
 */
void TF::addRoot(Complex root)
{
	m_roots.append(root);
	updateNumerator();
}

/*!
 Aggiungere un polo alla fdt necessita di due fasi.
	-# viene aggiunto effettivamente il poloe alla lista dei poli.
	-# viene ricalcolato il polinomio denominatore.
 */
void TF::addPole(Complex pole)
{
	m_poles.append(pole);
	updateDenominator();
}

/*!
 A differenza di addRoot(Complex), questa funzione imposta, modificandolo, il valore
 di uno zero già esistente. Il valore precedente viene sostituito.
 Alla fine, la fdt conterrà lo zero un numero di volte pari a order, e viene 
 ricalcolato il polinomio numeratore.
 
 \see updateNumerator()
 */
void TF::setRoot(int i, Complex root, int order)
{	
	if(i < m_roots.count())
	{
		removeRoot(i);
		for(int i = 0; i < order; i++)
			m_roots.insert(i, root);
	}
	updateNumerator();
}

/*!
 A differenza di addPole(Complex), questa funzione imposta, modificandolo, il valore
 di un polo già esistente. Il valore precedente viene sostituito.
 Alla fine, la fdt conterrà il polo un numero di volte pari a order, e viene 
 ricalcolato il polinomio denominatore.
 
 \see updateDenominator()
 */
void TF::setPole(int i, Complex pole, int order)
{
	if(i < m_poles.count())
	{
		removePole(i);
		for(int i = 0; i < order; i++)
			m_poles.insert(i, pole);
	}
	updateDenominator();
}

void TF::removeRoot(int i)
{
	Complex old = m_roots[i];
	
	m_roots.removeAll(old);
	updateNumerator();
}

void TF::removePole(int i)
{
	Complex old = m_poles[i];
	
	m_poles.removeAll(old);
	updateDenominator();
}

/*!
 Il polinomio numeratore viene calcolato come il prodotto di N binomi, tanti quanti
 gli zeri della funzione, ognuno nella forma (s - z).
 La moltiplicazione avviene utilizzando i metodi della classe Polynomial.
 
 \see Polynomial
 */
void TF::updateNumerator()
{
	Polynomial n = 1;
	
	for(int i = 0; i < m_roots.count(); i++)
	{
		Polynomial b; // Make binomial (s - z)
		b.addTerm(0, -m_roots[i]);
		b.addTerm(1, 1);
		
		n = n*b;
	}
	
	m_num = n;
}

/*!
 Il polinomio denominatore viene calcolato come il prodotto di N binomi, tanti quanti
 i poli della funzione, ognuno nella forma (s - z).
 La moltiplicazione avviene utilizzando i metodi della classe Polynomial.
 
 \see Polynomial
 */
void TF::updateDenominator()
{
	Polynomial p = 1;
	
	for(int i = 0; i < m_poles.count(); i++)
	{
		Polynomial b; // Make binomial (s - z)
		b.addTerm(0, -m_poles[i]);
		b.addTerm(1, 1);
		
		p = p*b;
	}
	
	m_den = p;
}

void TF::setNumerator(Polynomial num)
{
	m_num = num;
	m_roots = num.roots();
}

void TF::setDenominator(Polynomial den)
{
	m_den = den;
	m_poles = den.roots();
}

/*!
 Il valore della funzione di trasferimento corrispondente ad un certo valore di s
 viene calcolato come rapporto tra i valori in funzione di s, dei polinomi numeratore
 e denominatore, sfruttando le operazioni algebriche implementate per la classe Complex
 
 \see Complex
 */
Complex TF::eval(Complex s)
{
	return numerator().eval(s) / denominator().eval(s);
}


/*!
 Ritorna la funzione di trasferimento a catena chiusa.
 Data la funzione di trasferimento a catena aperta
 
				G(s) = N(s)/D(s)
 
 e il guadagno di reazione H, la funzione di trasferimento
 a catena chiusa (in caso di reazione negativa) è
 
				W(s) = 1 / ( 1 + HG(s) )
 
 Che può essere scritta anche in modo equivalente:
 
				W(s) = N(s) / ( D(s) + HN(s) )
 
 Questa funzione calcola la funzione di trasferimento a catena
 chiusa proprio in questo modo.
 Si può scegliere tramite il parametro rtype la reazione
 positiva piuttosto che negativa.
 Il quadagno di reazione è precisato nel parametro reaction
 */
TF TF::closedLoop(Real reaction, ReactionType rtype)
{
	return TF(m_num, m_den + m_num*reaction*(rtype == Positive ? 1 : -1));
}



